{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b5348f6d",
   "metadata": {},
   "source": [
    "### 代码实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "341a636b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入必要的库，torchinfo用于查看模型结构\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "from torchinfo import summary"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23b6e770",
   "metadata": {},
   "source": [
    "### 结构定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "05481332",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义VGGNet的网络结构\n",
    "class VGG(nn.Module):\n",
    "    def __init__(self, features, num_classes=1000):\n",
    "        super(VGG, self).__init__()\n",
    "        # 卷积层直接使用传入的结构，后面有专门构建这部分的内容\n",
    "        self.features = features\n",
    "        # 定义全连接层\n",
    "        self.classifier = nn.Sequential(\n",
    "            # 全连接层+ReLU+Dropout\n",
    "            nn.Linear(512 * 7 * 7, 4096),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.Dropout(),\n",
    "            # 全连接层+ReLU+Dropout\n",
    "            nn.Linear(4096, 4096),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.Dropout(),\n",
    "            # 全连接层\n",
    "            nn.Linear(4096, num_classes),\n",
    "        )\n",
    "\n",
    "    # 定义前向传播函数\n",
    "    def forward(self, x):\n",
    "        # 先经过feature提取特征，flatten后送入全连接层\n",
    "        x = self.features(x)\n",
    "        x = torch.flatten(x, 1)\n",
    "        x = self.classifier(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa812ad3",
   "metadata": {},
   "source": [
    "### 定义配置项"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "30357203",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义相关配置项，其中M表示池化层，数值完全对应论文中的表格数值\n",
    "cfgs = {\n",
    "    'vgg11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],\n",
    "    'vgg13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],\n",
    "    'vgg16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],\n",
    "    'vgg19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95821ca9",
   "metadata": {},
   "source": [
    "### 拼接卷积层"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "44d2692e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 根据传入的配置项拼接卷积层\n",
    "def make_layers(cfg):\n",
    "    layers = []\n",
    "    in_channels = 3 #初始通道数为3\n",
    "    # 遍历传入的配置项\n",
    "    for v in cfg:\n",
    "        if v == 'M': # 如果是池化层，则直接新增MaxPool2d即可\n",
    "            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]\n",
    "        else: # 如果是卷积层，则新增3x3卷积+ReLU\n",
    "            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)\n",
    "            layers += [conv2d, nn.ReLU(inplace=True)]\n",
    "            in_channels = v #记录通道数，作为下一次的in_channels\n",
    "    # 返回使用Sequential构造的卷积层\n",
    "    return nn.Sequential(*layers)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3672aaf1",
   "metadata": {},
   "source": [
    "### 封装函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "6a9fc781",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 封装函数，依次传入对应的配置项\n",
    "def vgg11(num_classes=1000):\n",
    "    return VGG(make_layers(cfgs['vgg11']), num_classes=num_classes)\n",
    "\n",
    "def vgg13(num_classes=1000):\n",
    "    return VGG(make_layers(cfgs['vgg13']), num_classes=num_classes)\n",
    "\n",
    "def vgg16(num_classes=1000):\n",
    "    return VGG(make_layers(cfgs['vgg16']), num_classes=num_classes)\n",
    "\n",
    "def vgg19(num_classes=1000):\n",
    "    return VGG(make_layers(cfgs['vgg19']), num_classes=num_classes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d068fda2",
   "metadata": {},
   "source": [
    "### 网络结构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c7c10b58",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "==========================================================================================\n",
       "Layer (type:depth-idx)                   Output Shape              Param #\n",
       "==========================================================================================\n",
       "VGG                                      [1, 1000]                 --\n",
       "├─Sequential: 1-1                        [1, 512, 7, 7]            --\n",
       "│    └─Conv2d: 2-1                       [1, 64, 224, 224]         1,792\n",
       "│    └─ReLU: 2-2                         [1, 64, 224, 224]         --\n",
       "│    └─Conv2d: 2-3                       [1, 64, 224, 224]         36,928\n",
       "│    └─ReLU: 2-4                         [1, 64, 224, 224]         --\n",
       "│    └─MaxPool2d: 2-5                    [1, 64, 112, 112]         --\n",
       "│    └─Conv2d: 2-6                       [1, 128, 112, 112]        73,856\n",
       "│    └─ReLU: 2-7                         [1, 128, 112, 112]        --\n",
       "│    └─Conv2d: 2-8                       [1, 128, 112, 112]        147,584\n",
       "│    └─ReLU: 2-9                         [1, 128, 112, 112]        --\n",
       "│    └─MaxPool2d: 2-10                   [1, 128, 56, 56]          --\n",
       "│    └─Conv2d: 2-11                      [1, 256, 56, 56]          295,168\n",
       "│    └─ReLU: 2-12                        [1, 256, 56, 56]          --\n",
       "│    └─Conv2d: 2-13                      [1, 256, 56, 56]          590,080\n",
       "│    └─ReLU: 2-14                        [1, 256, 56, 56]          --\n",
       "│    └─Conv2d: 2-15                      [1, 256, 56, 56]          590,080\n",
       "│    └─ReLU: 2-16                        [1, 256, 56, 56]          --\n",
       "│    └─MaxPool2d: 2-17                   [1, 256, 28, 28]          --\n",
       "│    └─Conv2d: 2-18                      [1, 512, 28, 28]          1,180,160\n",
       "│    └─ReLU: 2-19                        [1, 512, 28, 28]          --\n",
       "│    └─Conv2d: 2-20                      [1, 512, 28, 28]          2,359,808\n",
       "│    └─ReLU: 2-21                        [1, 512, 28, 28]          --\n",
       "│    └─Conv2d: 2-22                      [1, 512, 28, 28]          2,359,808\n",
       "│    └─ReLU: 2-23                        [1, 512, 28, 28]          --\n",
       "│    └─MaxPool2d: 2-24                   [1, 512, 14, 14]          --\n",
       "│    └─Conv2d: 2-25                      [1, 512, 14, 14]          2,359,808\n",
       "│    └─ReLU: 2-26                        [1, 512, 14, 14]          --\n",
       "│    └─Conv2d: 2-27                      [1, 512, 14, 14]          2,359,808\n",
       "│    └─ReLU: 2-28                        [1, 512, 14, 14]          --\n",
       "│    └─Conv2d: 2-29                      [1, 512, 14, 14]          2,359,808\n",
       "│    └─ReLU: 2-30                        [1, 512, 14, 14]          --\n",
       "│    └─MaxPool2d: 2-31                   [1, 512, 7, 7]            --\n",
       "├─Sequential: 1-2                        [1, 1000]                 --\n",
       "│    └─Linear: 2-32                      [1, 4096]                 102,764,544\n",
       "│    └─ReLU: 2-33                        [1, 4096]                 --\n",
       "│    └─Dropout: 2-34                     [1, 4096]                 --\n",
       "│    └─Linear: 2-35                      [1, 4096]                 16,781,312\n",
       "│    └─ReLU: 2-36                        [1, 4096]                 --\n",
       "│    └─Dropout: 2-37                     [1, 4096]                 --\n",
       "│    └─Linear: 2-38                      [1, 1000]                 4,097,000\n",
       "==========================================================================================\n",
       "Total params: 138,357,544\n",
       "Trainable params: 138,357,544\n",
       "Non-trainable params: 0\n",
       "Total mult-adds (G): 15.48\n",
       "==========================================================================================\n",
       "Input size (MB): 0.60\n",
       "Forward/backward pass size (MB): 108.45\n",
       "Params size (MB): 553.43\n",
       "Estimated Total Size (MB): 662.49\n",
       "=========================================================================================="
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看模型结构及参数量，input_size表示示例输入数据的维度信息\n",
    "summary(vgg16(), input_size=(1, 3, 224, 224))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4511593d",
   "metadata": {},
   "source": [
    "### torchvision"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "c0ef4434",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "==========================================================================================\n",
       "Layer (type:depth-idx)                   Output Shape              Param #\n",
       "==========================================================================================\n",
       "VGG                                      [1, 1000]                 --\n",
       "├─Sequential: 1-1                        [1, 512, 7, 7]            --\n",
       "│    └─Conv2d: 2-1                       [1, 64, 224, 224]         1,792\n",
       "│    └─ReLU: 2-2                         [1, 64, 224, 224]         --\n",
       "│    └─Conv2d: 2-3                       [1, 64, 224, 224]         36,928\n",
       "│    └─ReLU: 2-4                         [1, 64, 224, 224]         --\n",
       "│    └─MaxPool2d: 2-5                    [1, 64, 112, 112]         --\n",
       "│    └─Conv2d: 2-6                       [1, 128, 112, 112]        73,856\n",
       "│    └─ReLU: 2-7                         [1, 128, 112, 112]        --\n",
       "│    └─Conv2d: 2-8                       [1, 128, 112, 112]        147,584\n",
       "│    └─ReLU: 2-9                         [1, 128, 112, 112]        --\n",
       "│    └─MaxPool2d: 2-10                   [1, 128, 56, 56]          --\n",
       "│    └─Conv2d: 2-11                      [1, 256, 56, 56]          295,168\n",
       "│    └─ReLU: 2-12                        [1, 256, 56, 56]          --\n",
       "│    └─Conv2d: 2-13                      [1, 256, 56, 56]          590,080\n",
       "│    └─ReLU: 2-14                        [1, 256, 56, 56]          --\n",
       "│    └─Conv2d: 2-15                      [1, 256, 56, 56]          590,080\n",
       "│    └─ReLU: 2-16                        [1, 256, 56, 56]          --\n",
       "│    └─MaxPool2d: 2-17                   [1, 256, 28, 28]          --\n",
       "│    └─Conv2d: 2-18                      [1, 512, 28, 28]          1,180,160\n",
       "│    └─ReLU: 2-19                        [1, 512, 28, 28]          --\n",
       "│    └─Conv2d: 2-20                      [1, 512, 28, 28]          2,359,808\n",
       "│    └─ReLU: 2-21                        [1, 512, 28, 28]          --\n",
       "│    └─Conv2d: 2-22                      [1, 512, 28, 28]          2,359,808\n",
       "│    └─ReLU: 2-23                        [1, 512, 28, 28]          --\n",
       "│    └─MaxPool2d: 2-24                   [1, 512, 14, 14]          --\n",
       "│    └─Conv2d: 2-25                      [1, 512, 14, 14]          2,359,808\n",
       "│    └─ReLU: 2-26                        [1, 512, 14, 14]          --\n",
       "│    └─Conv2d: 2-27                      [1, 512, 14, 14]          2,359,808\n",
       "│    └─ReLU: 2-28                        [1, 512, 14, 14]          --\n",
       "│    └─Conv2d: 2-29                      [1, 512, 14, 14]          2,359,808\n",
       "│    └─ReLU: 2-30                        [1, 512, 14, 14]          --\n",
       "│    └─MaxPool2d: 2-31                   [1, 512, 7, 7]            --\n",
       "├─AdaptiveAvgPool2d: 1-2                 [1, 512, 7, 7]            --\n",
       "├─Sequential: 1-3                        [1, 1000]                 --\n",
       "│    └─Linear: 2-32                      [1, 4096]                 102,764,544\n",
       "│    └─ReLU: 2-33                        [1, 4096]                 --\n",
       "│    └─Dropout: 2-34                     [1, 4096]                 --\n",
       "│    └─Linear: 2-35                      [1, 4096]                 16,781,312\n",
       "│    └─ReLU: 2-36                        [1, 4096]                 --\n",
       "│    └─Dropout: 2-37                     [1, 4096]                 --\n",
       "│    └─Linear: 2-38                      [1, 1000]                 4,097,000\n",
       "==========================================================================================\n",
       "Total params: 138,357,544\n",
       "Trainable params: 138,357,544\n",
       "Non-trainable params: 0\n",
       "Total mult-adds (G): 15.48\n",
       "==========================================================================================\n",
       "Input size (MB): 0.60\n",
       "Forward/backward pass size (MB): 108.45\n",
       "Params size (MB): 553.43\n",
       "Estimated Total Size (MB): 662.49\n",
       "=========================================================================================="
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看torchvision自带的模型结构及参数量\n",
    "from torchvision import models\n",
    "summary(models.vgg16(), input_size=(1, 3, 224, 224))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "491f1186",
   "metadata": {},
   "source": [
    "### 模型训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "216b5d0f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 0 Loss: 2.637844527695176 Acc: 0.018627450980392157\n",
      "Epoch: 10 Loss: 2.4838707358055605 Acc: 0.1264705882352941\n",
      "Epoch: 20 Loss: 2.402153071079769 Acc: 0.27450980392156865\n",
      "Epoch: 30 Loss: 2.342568576928816 Acc: 0.38235294117647056\n",
      "Epoch: 40 Loss: 2.2866669604723584 Acc: 0.4068627450980392\n",
      "Epoch: 50 Loss: 2.2222905615782875 Acc: 0.48627450980392156\n",
      "Epoch: 60 Loss: 2.1768020231861245 Acc: 0.5294117647058824\n",
      "Epoch: 70 Loss: 2.129151349436138 Acc: 0.5725490196078431\n",
      "Epoch: 80 Loss: 2.0938160879230883 Acc: 0.5480392156862746\n",
      "Epoch: 90 Loss: 2.0742531937191417 Acc: 0.615686274509804\n",
      "Epoch: 100 Loss: 1.9786159326027757 Acc: 0.6323529411764706\n",
      "Epoch: 110 Loss: 1.9387913170755713 Acc: 0.6401960784313725\n",
      "Epoch: 120 Loss: 1.9101603846551043 Acc: 0.6245098039215686\n",
      "Epoch: 130 Loss: 1.8723950972052152 Acc: 0.6401960784313725\n",
      "Epoch: 140 Loss: 1.8498576993314426 Acc: 0.6490196078431373\n",
      "Epoch: 150 Loss: 1.8075653947559336 Acc: 0.6843137254901961\n",
      "Epoch: 160 Loss: 1.7633844844349251 Acc: 0.6774509803921569\n",
      "Epoch: 170 Loss: 1.7869407693113888 Acc: 0.6627450980392157\n",
      "Epoch: 180 Loss: 1.7310580976349765 Acc: 0.6911764705882353\n",
      "Epoch: 190 Loss: 1.6981222229467665 Acc: 0.6696078431372549\n",
      "100%|██████████| 200/200 [1:42:13<00:00, 30.67s/it]\n"
     ]
    },
    {
     "data": {
      "image/png": "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K6+8wrPPPsv27duZPn06oaGhANx3331Mnz69WOvEV199RXh4OH379i1z+UqrTM00Dz74INOnT+enn36iadOmhcv9/Pzw9DSz206cOJG4uDi++OILACZPnkz9+vVp2bIlOTk5TJs2jZdffpkZM2YUiwLPpqJm7b0Y0rPzWLQzkb/2JPPL5kOkZeUR7OPOf69rQ+/GwTip+UZELmElVvHnZMBL4RVfmH8cArfzHxV55MgRQkJC2LJlC8uXL2fChAns37+/xEEe3bt3p0GDBkybNq3EY9lsNmbNmsXw4cMLl/n7+zN58mTuuusu9u/fT2RkJJMnT+bRRx89a7muvvpqmjdvzmuvvUZaWhrBwcG888473Hfffadtm52dTXh4OO+//z433HADYAagDB8+nOeee67E45dHM02Zhva+//77APTp06fY8k8//ZS77roLgPj4eGJiYgrX5eTkMGHCBOLi4vD09KRly5bMmTOHwYMHl+XUVVYNdxeGtAlnSJtwHujVgPu/XMuuw+nc/eka6gV6MaJ9Hfo0DaZVbT/1KxERqUL27t3Ls88+y8qVK0lKSiqs9YiJiWHjxo20b9/+jKNNN27cyKhRoy64DH+vTcnPz+fll1/m22+/JS4urrAPpre3CbKioqLIzs6mf//+JR7P3d2d2267jalTp3LDDTewceNGNm3adFqTUXkrUzBSmkqUgvasAk888QRPPPFEmQpVXdUP8mbWg5fz3992MGN9HAeSM3nzj128+ccuArzdGNu3EXd0q4eLs7L0i8glzNXL1FI44rxlMHToUCIiIvjoo48IDw/HbrfTqlUrcnJyClsLzuRc620222nP3Nzc0ydrLQgyCrz++uu8+eabTJ48mdatW+Pt7c24cePIyckp1XnBNNW0a9eOgwcPMnXqVPr370+9evXOud+F0FOvgnm7u/DCsFasfro/r13flqta1sLHw4WjGTm8+Mt2rn5rGav2JTu6mCIijmOzmeaSin7ZSl87nZycTFRUFM888wz9+/enefPmHDt2rHB9mzZt2LhxI0ePlpx7qk2bNvz5559nPH5wcDDx8fGFn3fv3k1m5rkzfi9dupRhw4Zx22230bZtWxo0aFBswEjjxo3x9PQ867lbt25Np06d+Oijj5g+fTr33HPPOc97ocpUMyLlx8vNhes61uG6jnXIy7fz3dqD/Pf3Hew8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zJjBjBkzyu8qREREpMoqczCydu1a+vbtW/h5/PjxANx555189tlnxMfHExMTU7g+JyeHCRMmEBcXh6enJy1btmTOnDkMHjy4HIovIiIiVd0FdWCtKKXtACMiIiKVR6XuwCoiIiJSQMGIiIiIOJSCEREREXEoBSMiIiLiUApGRERExKEUjIiIiIhDKRgRERERh1IwIiIiIg6lYEREREQcSsGIiIiIOJSCEREREXEoBSMiIiLiUApGRERExKEUjIiIiIhDKRgRERERh1IwIiIiIg6lYEREREQcSsGIiIiIOJSCEREREXEoBSMiIiLiUApGRERExKEUjIiIiIhDKRgRERERh1IwIiIiIg6lYEREREQcSsGIiIiIOJSCEREREXEoBSMiIiLiUApGRERExKEUjIiIiIhDKRgRERERh1IwIiIiIg6lYEREREQcSsGIiIiIOJSCEREREXEoBSMiIiLiUApGRERExKEUjIiIiIhDKRgRERERh1IwIiIiIg6lYEREREQcSsGIiIiIOJSCEREREXEoBSMiIiLiUApGRERExKEUjIiIiIhDKRgRERERh1IwIiIiIg6lYEREREQcSsGIiIiIOJSCEREREXEoBSMiIiLiUApGRERExKHKHIwsWbKEoUOHEh4ejs1m48cffzznPosXL6Zjx454eHjQoEEDPvjgg/Mpq4iIiFRDZQ5GMjIyaNu2Le+8806pto+Ojmbw4MH07NmTDRs28I9//INHHnmEGTNmlLmwIiIiUv24lHWHQYMGMWjQoFJv/8EHH1C3bl0mT54MQPPmzVm7di2vvfYaI0eOLOvpRUREpJq56H1GVqxYwYABA4otGzhwIGvXriU3N/din15EREQquTLXjJRVQkICoaGhxZaFhoaSl5dHUlISYWFhp+2TnZ1NdnZ24efU1NSLXUwRERFxkAoZTWOz2Yp9tiyrxOUFJk2ahJ+fX+ErIiLiopdRREREHOOiByO1atUiISGh2LLExERcXFwIDAwscZ+JEyeSkpJS+IqNjb3YxRQREREHuejNNN26dePnn38utmzevHl06tQJV1fXEvdxd3fH3d39YhdNREREKoEy14ykp6ezceNGNm7cCJihuxs3biQmJgYwtRp33HFH4fajR4/mwIEDjB8/nqioKKZOnconn3zChAkTyucKREREpEorc83I2rVr6du3b+Hn8ePH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\n",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy: 0.7176470588235294\n"
     ]
    }
   ],
   "source": [
    "# 导入必要的库\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.optim as optim\n",
    "from torch.utils.data import DataLoader\n",
    "from torchvision import datasets, transforms, models\n",
    "from tqdm import *\n",
    "import numpy as np\n",
    "import sys\n",
    "\n",
    "# 设备检测，若未检测到cuda设备则在CPU上运行\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "\n",
    "# 设置随机种子\n",
    "torch.manual_seed(0)\n",
    "\n",
    "# 定义模型、优化器、损失函数\n",
    "model = vgg11(num_classes=102).to(device)\n",
    "optimizer = optim.SGD(model.parameters(), lr=0.002, momentum=0.9)\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "\n",
    "# 设置训练集的数据变换，进行数据增强\n",
    "trainform_train = transforms.Compose([\n",
    "    transforms.RandomRotation(30), # 随机旋转 -30度到30度之间\n",
    "    transforms.RandomResizedCrop((224, 224)), # 随机比例裁剪并进行resize\n",
    "    transforms.RandomHorizontalFlip(p = 0.5), # 随机水平翻转\n",
    "    transforms.RandomVerticalFlip(p = 0.5), # 随机垂直翻转\n",
    "    transforms.ToTensor(),  # 将数据转换为张量\n",
    "    # 对三通道数据进行归一化(均值，标准差)，数值是从ImageNet数据集上的百万张图片中随机抽样计算得到\n",
    "    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n",
    "])\n",
    "\n",
    "# 设置测试集的数据变换，不进行数据增强，仅使用resize和归一化\n",
    "transform_test = transforms.Compose([\n",
    "    transforms.Resize((224, 224)),  # resize\n",
    "    transforms.ToTensor(),  # 将数据转换为张量\n",
    "    # 对三通道数据进行归一化(均值，标准差)，数值是从ImageNet数据集上的百万张图片中随机抽样计算得到\n",
    "    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n",
    "])\n",
    "\n",
    "# 加载训练数据，需要特别注意的是Flowers102数据集，test簇的数据量较多些，所以这里使用\"test\"作为训练集\n",
    "train_dataset = datasets.Flowers102(root='../data/flowers102', split=\"test\", download=True, transform=trainform_train)\n",
    "# 实例化训练数据加载器\n",
    "train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=4)\n",
    "# 加载测试数据，使用\"train\"作为测试集\n",
    "test_dataset = datasets.Flowers102(root='../data/flowers102', split=\"train\", download=True, transform=transform_test)\n",
    "# 实例化测试数据加载器\n",
    "test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=4)\n",
    "\n",
    "# 设置epoch数并开始训练\n",
    "num_epochs = 200  # 设置epoch数\n",
    "loss_history = []  # 创建损失历史记录列表\n",
    "acc_history = []   # 创建准确率历史记录列表\n",
    "\n",
    "# tqdm用于显示进度条并评估任务时间开销\n",
    "for epoch in tqdm(range(num_epochs), file=sys.stdout):\n",
    "    # 记录损失和预测正确数\n",
    "    total_loss = 0\n",
    "    total_correct = 0\n",
    "    \n",
    "    # 批量训练\n",
    "    model.train()\n",
    "    for inputs, labels in train_loader:\n",
    "        # 将数据转移到指定计算资源设备上\n",
    "        inputs = inputs.to(device)\n",
    "        labels = labels.to(device)\n",
    "        \n",
    "        # 预测、损失函数、反向传播\n",
    "        optimizer.zero_grad()\n",
    "        outputs = model(inputs)\n",
    "        loss = criterion(outputs, labels)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        # 记录训练集loss\n",
    "        total_loss += loss.item()\n",
    "    \n",
    "    # 测试模型，不计算梯度\n",
    "    model.eval()\n",
    "    with torch.no_grad():\n",
    "        for inputs, labels in test_loader:\n",
    "            # 将数据转移到指定计算资源设备上\n",
    "            inputs = inputs.to(device)\n",
    "            labels = labels.to(device)\n",
    "            \n",
    "            # 预测\n",
    "            outputs = model(inputs)\n",
    "            # 记录测试集预测正确数\n",
    "            total_correct += (outputs.argmax(1) == labels).sum().item()\n",
    "        \n",
    "    # 记录训练集损失和测试集准确率\n",
    "    loss_history.append(np.log10(total_loss))  # 将损失加入损失历史记录列表，由于数值有时较大，这里取对数\n",
    "    acc_history.append(total_correct / len(test_dataset))# 将准确率加入准确率历史记录列表\n",
    "    \n",
    "    # 打印中间值\n",
    "    if epoch % 10 == 0:\n",
    "        tqdm.write(\"Epoch: {0} Loss: {1} Acc: {2}\".format(epoch, loss_history[-1], acc_history[-1]))\n",
    "\n",
    "# 使用Matplotlib绘制损失和准确率的曲线图\n",
    "import matplotlib.pyplot as plt\n",
    "plt.plot(loss_history, label='loss')\n",
    "plt.plot(acc_history, label='accuracy')\n",
    "plt.legend()\n",
    "plt.show()\n",
    "\n",
    "# 输出准确率\n",
    "print(\"Accuracy:\", acc_history[-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2879f7f1",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
